﻿using System;
using System.Web.UI;
using System.Web.UI.WebControls;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Xml;
using Microsoft.SharePoint.WebPartPages;

namespace CustomWebPartZone
{
    /// <summary>
    /// Use this control to define the way you want web parts & user controls that are added to a web part zone to be added to a page.
    /// If you want to be able to add user controls to the page, you will need a text field/column in the SharePoint Page that will be used to define the user controls to be added to this page
    /// If there are multiple user controls that are required to be added to the page, seperate them with a ";"
    /// To use this web part zone in a page layout, register the control by adding te below line to the page layout
    /// <%@ Register TagPrefix="Custom" TagName="WebPartZone" Src="~/_controltemplates/CustomWebPartZone/CustomWebPartZone.ascx" %>
    /// Add the control to the page layout at the desired location 	
    /// <div id="divCustomWPZone">
    /// 	<Custom:WebPartZone runat="server" id="zoneCustom" DisplayTitle="Custom Zone" LoadWebPartZone="true"/>
    /// </div>
    /// If you want be to able to load user controls as well, just use the FieldName="Name of the page field that contains the user control(s) to add to the page"
    /// </summary>
    public partial class CustomWebPartZone : UserControl
    {
        #region Child Controls
        protected global::Microsoft.SharePoint.WebPartPages.WebPartZone CustomWPZone;
        #endregion 

        #region Control Fields
        const String ControlTemplatePath = "/_controltemplates/";

        /// <summary>
        /// Whether to load web part zone or not?
        /// </summary>
        public Boolean LoadWebPartZone { get; set; }

        /// <summary>
        /// Display title for the web part zone
        /// </summary>
        public String DisplayTitle { get; set; }
        
        /// <summary>
        /// Name of the field that contains the user control name / list of user control names to display in the web part zone
        /// </summary>
        public String FieldName { get; set; }
        #endregion

        #region Properties
        /// <summary>
        /// The display mode of current page
        /// </summary>
        protected bool InDisplayMode
        {
            get
            {
                return SPContext.Current.FormContext.FormMode == SPControlMode.Display;
            }
        }
        #endregion

        #region Control Events
        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);

            if (LoadWebPartZone)
            {
                //If the web part zone is required, load it into the panel

                CustomWPZone = new Microsoft.SharePoint.WebPartPages.WebPartZone();
                CustomWPZone.ID = String.Format("{0}_CustomWPZone", this.ClientID);
                CustomWPZone.HeaderText = DisplayTitle;
                CustomWPZone.EnableViewState = true;
                CustomWPZone.PartChromeType = System.Web.UI.WebControls.WebParts.PartChromeType.None;
                plControl.Controls.Add(CustomWPZone);
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            try
            {
                //Read the user control(s) from the field and load them
                SPSecurity.RunWithElevatedPrivileges(ProcessPageControlField);
            }
            catch { }
        }

        protected override void CreateChildControls()
        {
            if (InDisplayMode)
            {
                if (LoadWebPartZone)
                    ProcessWebPartsInZone();
            }
            else
            {
                base.CreateChildControls();
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            EnsureChildControls();
            if (InDisplayMode && LoadWebPartZone)
            {
                //If the page is in display mode, hide the web part zone
                CustomWPZone.Visible = false;
            }
            else
            {
                //If the page is NOT in display mode, show the web part zone
                CustomWPZone.Visible = true;
            }
            base.Render(writer);
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Read the user control(s) from the field and load them
        /// </summary>
        void ProcessPageControlField()
        {
            try
            {
                if (null != FieldName)
                {
                    if (!String.IsNullOrEmpty(FieldName))
                    {
                        if (SPContext.Current.ListItem.Fields.ContainsField(FieldName))
                        {
                            //Read data from the field that contains the user controls
                            String PageControls = GetString(SPContext.Current.ListItem[FieldName]);
                            if (!String.IsNullOrEmpty(PageControls))
                            {
                                // ; delimited list, split it and add
                                if (PageControls.Contains(";"))
                                {
                                    foreach (String pageControlPath in PageControls.Split(';'))
                                        AddDynamicPageControl(plControl, pageControlPath);
                                }
                                //single control, add it to placeholder
                                else
                                {
                                    AddDynamicPageControl(plControl, PageControls);
                                }
                            }
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                //Do what is needed with the exception
            }
        }

        /// <summary>
        /// Add a user control to the page
        /// </summary>
        /// <param name="parentControl"></param>
        /// <param name="controlUrl"></param>
        void AddDynamicPageControl(Control parentControl, String controlUrl)
        {
            //This is where you define/control how the user controls are added to the parent
            //In this sample code, I have just added them one after the other.
            //You might want to add each user control in a new panel, apply css to them / seperate them with a <hr> / whatever you want to do :)
            if (!String.IsNullOrEmpty(controlUrl.Trim()))
                parentControl.Controls.Add(Page.LoadControl(String.Concat(ControlTemplatePath, controlUrl.Trim())));
        }

        /// <summary>
        /// Read the web parts from the web part zone and add them to the custom zone panel
        /// </summary>
        void ProcessWebPartsInZone()
        {
            try
            {
                System.Web.UI.WebControls.WebParts.WebPartCollection webParts = null;

                SPSecurity.RunWithElevatedPrivileges(delegate()
                {
                    webParts = this.CustomWPZone.WebParts;
                });

                //Add each web part contained in this web part zone
                foreach (WebPart part in webParts)
                {
                    try
                    {
                        //This is where you define/control how the web parts are added to the page.
                        //In this sample code, I have just added web parts one after the other.
                        //You might want to add each web part in a new panel, apply css to them / seperate them with a <hr> / whatever you want to do :)
                        plControl.Controls.Add(part);
                    }
                    catch (Exception ex)
                    {
                        //Eat the error. This is intended behaviour in SP 2010
                    }
                }
            }
            catch (Exception ex)
            {
                //Do what is needed to be done with the exception here
            }
        }

        /// <summary>
        /// Cast an Object to a String
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public static String GetString(object o)
        {
            if (o != null)
                return o.ToString();

            return String.Empty;
        }
        #endregion 
    }
}
